package serialization;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtobufIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ProtostuffSerialize implements ISerialize {

	private static final Map<Class, Schema> map = new ConcurrentHashMap<Class, Schema>();

	private static final Schema<SerializeObjectWrapper> schema = RuntimeSchema.getSchema(SerializeObjectWrapper.class);

	private ThreadLocal<LinkedBuffer> linkedBufferThreadLocal = new ThreadLocal<>();
	
	@SuppressWarnings("unchecked")
	public byte[] serialize(Object obj) {
//		Schema sc = getSchema(obj.getClass());
		SerializeObjectWrapper wrapper = new SerializeObjectWrapper(obj);
		LinkedBuffer buffer = linkedBufferThreadLocal.get();
		if(buffer==null)
		{
			buffer = LinkedBuffer.allocate(4096);
			linkedBufferThreadLocal.set(buffer);
		}
		byte[] protostuff = null;
		try {
			protostuff = ProtobufIOUtil.toByteArray(wrapper, schema, buffer);
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			buffer.clear();
		}
		return protostuff;
	}

	@SuppressWarnings("unchecked")
	public <T> T unSerialize(byte[] byts, Class<T> t) {
		SerializeObjectWrapper wrapper = new SerializeObjectWrapper();
//		Schema sc = getSchema(t);
		ProtobufIOUtil.mergeFrom(byts, wrapper, schema);
		return (T) wrapper.getObject();
	}

	private Schema getSchema(Class cls) {
		Schema sc = map.get(cls);
		if (sc == null) {
			sc = RuntimeSchema.getSchema(cls);
			map.put(cls, sc);
		}
		return sc;
	}
	
	public static void main(String[] args) {
		List<Object> obj = new ArrayList<>();
		//obj.add(new Date());
		obj.add(1);
		obj.add(23434);
		obj.add(23434);
		obj.add(23434);
		obj.add(1);


		ISerialize s = new ProtostuffSerialize();
		ISerialize s1 = new JsonSerialize();
		ISerialize s2 = new Hessian2Serialization();
		ISerialize s3 = new FstSerialize();
		ISerialize s4 = new KryoSerialize();
		
		
		byte[] b = test(obj, s);
		System.out.println(b.length);
		b = test(obj, s1);
		System.out.println(b.length);
		b = test(obj, s2);
		System.out.println(b.length);
		b = test(obj, s3);
		System.out.println(b.length);
		b = test(obj, s4);
		System.out.println(b.length);
		
		for(int i =0;i<1000000;i++)
		{
			test(obj, s);
			test(obj, s1);
			test(obj, s2);
			test(obj, s3);
			test(obj, s4);
		}
		
		
		long start = System.currentTimeMillis();
		for (int i = 0; i < 1000000; i++) {
			test(obj, s);
		}
		System.out.println(System.currentTimeMillis()-start);
		

		
		start = System.currentTimeMillis();
		for (int i = 0; i < 1000000; i++) {
			test(obj, s1);
		}
		System.out.println(System.currentTimeMillis()-start);
		
		
		start = System.currentTimeMillis();
		for (int i = 0; i < 1000000; i++) {
			test(obj, s2);
		}
		System.out.println(System.currentTimeMillis()-start);
		
		
		start = System.currentTimeMillis();
		for (int i = 0; i < 1000000; i++) {
			test(obj, s3);
		}
		System.out.println(System.currentTimeMillis()-start);
		start = System.currentTimeMillis();
		for (int i = 0; i < 1000000; i++) {
			test(obj, s4);
		}
		System.out.println(System.currentTimeMillis()-start);
		
//		s = new ProtostuffSerialize();
//		start = System.currentTimeMillis();
//		for (int i = 0; i < 100000; i++) {
//			test(obj, s);
//		}
//		System.out.println(System.currentTimeMillis()-start);
		
		
		
	}
	
	private static byte[] test(List<Object> obj,ISerialize s)
	{
		byte[] bytes = s.serialize(obj);
		obj= s.unSerialize(bytes, ArrayList.class);
		return bytes;
	}
	
	

}
